/**   
 * @Title: HttpUtil.java 
 * @Package me.crawler.util
 * @Description: TODO
 * @author weiwei
 * @date 2015-12-2 下午2:37:25 
 * @version V1.0   
 */
package me.crawler.util;

import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSession;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * @author weiwei
 * @Date 2015-12-2
 */
@Slf4j
public class HttpUtil implements Serializable {

	private static final long serialVersionUID = 4564912612466111417L;

	public final static String CONTENT_TYPE_APPLICATION_JSON = "application/json;charset=utf-8";

	/** SOAP请求格式 **/
	public final static String CONTENT_TYPE_APPLICATION_SOAP_XML = "application/soap+xml;charset=utf-8";

	public final static String CONTENT_TYPE_TEXT_XML = "text/xml;charset=utf-8";

	public final static String DEFAULT_UA = "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:43.0) Gecko/20100101 Firefox/43.0";

	private final static String UA_ARR[] = {"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
			"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:49.0) Gecko/20100101 Firefox/49.0",
			"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.80 Safari/537.36 Core/1.47.933.400 QQBrowser/9.4.8699.400",
			"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36",
			"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/602.2.14 (KHTML, like Gecko) Version/10.0.1 Safari/602.2.14",
			"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36",
			"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Safari/602.1.50",
			"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Win64; x64; Trident/4.0; .NET CLR 2.0.50727; SLCC2; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E; Tablet PC 2.0)"};

	private final static int DEFAULT_TIMEOUT = 60000;

	private final static int CONNECTION_REQUEST_TIMEOUT = DEFAULT_TIMEOUT;

	private final static int CONNECT_TIMEOUT = DEFAULT_TIMEOUT;

	private final static int SOCKET_TIMEOUT = DEFAULT_TIMEOUT;

	private static boolean initizlized = false;

	private RequestConfig config = null;

	private HttpClientBuilder httpClientBuilder = null;

	private HttpUtil() {
		synchronized (HttpUtil.class) {
			if (initizlized == false) {
				init();
				initizlized = !initizlized;
			} else {
				throw new RuntimeException("获取对象异常::已被破坏");
			}
		}
	}

	public static HttpUtil getInstance() {
		return SingletonHolder.INSTANCE;
	}

	static class SingletonHolder {
		private final static HttpUtil INSTANCE = new HttpUtil();
	}

	/**
	 * @Title: init
	 * @Description: 初始化
	 * @return void
	 */
	private void init() {
		try {
			config = RequestConfig.custom().setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT).setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();
			boolean flag = false;
			httpClientBuilder = HttpClientBuilder.create().setDefaultRequestConfig(config)
					.setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build(),
							new String[]{"TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3"}, null, new HostnameVerifier() {
								@Override
								public boolean verify(String arg0, SSLSession arg1) {
									return true;
								}
							}));
			if (flag) {
				httpClientBuilder = HttpClientBuilder.create().setSSLHostnameVerifier(new HostnameVerifier() {
					@Override
					public boolean verify(String arg0, SSLSession arg1) {
						return true;
					}
				}).setDefaultRequestConfig(config);
			}
		} catch (KeyManagementException e) {
			log.error(e.getMessage(), e);
		} catch (NoSuchAlgorithmException e) {
			log.error(e.getMessage(), e);
		} catch (KeyStoreException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		}
	}

	/**
	 * @Title: createHttpClient
	 * @Description: 创建HttpClient
	 * @param userAgent
	 * @return CloseableHttpClient
	 */
	public CloseableHttpClient createHttpClient(String userAgent) {
		if (null == httpClientBuilder) {
			init();
		}
		return httpClientBuilder.setUserAgent(userAgent).build();
	}

	/**
	 * @Title: get
	 * @Description: Httpclient get请求
	 * @param url
	 * @return String
	 */
	public String get(String url) {
		return get(url, null);
	}

	/**
	 * @Title: get
	 * @Description: Httpclient get请求
	 * @param url
	 * @param ua
	 * @return String
	 */
	public String get(String url, String ua) {
		return get(url, ua, "");
	}

	/**
	 * @Title: get
	 * @Description: Httpclient get请求
	 * @param url
	 * @param ua
	 * @param cookie
	 * @return String
	 */
	public String get(String url, String ua, String cookie) {
		Map<String, String> headerParams = null;
		if (StringUtils.isNotBlank(cookie)) {
			headerParams = new ConcurrentHashMap<String, String>(1);
			headerParams.put("Cookie", cookie);
		}
		return get(url, ua, headerParams);
	}

	/**
	 * @Title: get
	 * @Description: Httpclient get请求
	 * @param url
	 * @param ua
	 * @param headerParams
	 * @return String
	 */
	public String get(String url, String ua, Map<String, String> headerParams) {
		log.debug(">>>>>>>> url >> {}, ua >> {}", url, ua);
		log.debug(">>>>>>>> headerParams >> {}", headerParams);
		String httpStr = null;
		CloseableHttpClient httpclient = null;
		HttpGet httpGet = null;
		CloseableHttpResponse response = null;
		try {
			httpclient = createHttpClient(ua);
			httpGet = new HttpGet(url);
			if (MapUtils.isNotEmpty(headerParams)) {
				for (Map.Entry<String, String> entry : headerParams.entrySet()) {
					String key = entry.getKey();
					String value = entry.getValue();
					httpGet.addHeader(key, value);
				}
			}
			response = httpclient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if (entity != null) {
				InputStream instream = entity.getContent();
				httpStr = IOUtils.toString(instream, "UTF-8");
			}
		} catch (ClientProtocolException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			close(httpclient, response, httpGet);
		}
		return httpStr;
	}

	/**
	 * @Title: post
	 * @Description: Httpclient post请求
	 * @param url
	 * @param params
	 * @return String
	 */
	public String post(String url, Map<String, Object> params) {
		return post(url, params, null);
	}

	/**
	 * @Title: post
	 * @Description: Httpclient post请求
	 * @param url
	 * @param params
	 * @param ua
	 * @return String
	 */
	public String post(String url, Map<String, Object> params, String ua) {
		return post(url, params, ua, "");
	}

	/**
	 * @Title: post
	 * @Description: Httpclient post请求
	 * @param url
	 * @param params
	 * @param ua
	 * @param cookie
	 * @return String
	 */
	public String post(String url, Map<String, Object> params, String ua, String cookie) {
		Map<String, String> headerParams = null;
		if (StringUtils.isNotBlank(cookie)) {
			headerParams = new ConcurrentHashMap<String, String>(1);
			headerParams.put("Cookie", cookie);
		}
		return post(url, params, ua, headerParams);
	}

	/**
	 * @Title: post
	 * @Description: Httpclient post请求
	 * @param url
	 * @param params
	 * @param ua
	 * @param cookie
	 * @return String
	 */
	public String post(String url, Map<String, Object> params, String ua, Map<String, String> headerParams) {
		log.debug(">>>>>>>> url >> {}", url);
		log.debug(">>>>>>>> params >> {}, headerParams >> {}, ua >> {}", params, headerParams, ua);
		String httpStr = null;
		CloseableHttpClient httpclient = null;
		CloseableHttpResponse response = null;
		HttpPost httpPost = null;
		try {
			httpclient = createHttpClient(ua);
			httpPost = new HttpPost(url);
			if (MapUtils.isNotEmpty(headerParams)) {
				for (Map.Entry<String, String> entry : headerParams.entrySet()) {
					String key = entry.getKey();
					String value = entry.getValue();
					httpPost.addHeader(key, value);
				}
			}
			List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
			for (Map.Entry<String, Object> entry : params.entrySet()) {
				NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry.getValue().toString());
				pairList.add(pair);
			}
			httpPost.setEntity(new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8")));
			response = httpclient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			httpStr = EntityUtils.toString(entity, "UTF-8");
		} catch (ClientProtocolException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			close(httpclient, response, httpPost);
		}
		return httpStr;
	}

	/**
	 * @Title: postRequestBody
	 * @Description: TODO
	 * @param bodyStr
	 * @param url
	 * @param contentType
	 * @return String
	 */
	public String postRequestBody(String bodyStr, String url, String contentType) {
		return postRequestBody(bodyStr, url, contentType, null);
	}

	/**
	 * @Title: postRequestBody
	 * @Description: TODO
	 * @param bodyStr
	 * @param url
	 * @param contentType
	 * @param object
	 * @return String
	 */
	private String postRequestBody(String bodyStr, String url, String contentType, String authorization) {
		return postRequestBody(bodyStr, url, contentType, authorization, null);
	}

	/**
	 * @Title: postRequestBody
	 * @Description: TODO
	 * @param bodyStr
	 * @param url
	 * @param contentType
	 * @param authorization
	 * @param ua
	 * @param cookie
	 * @return String
	 */
	public String postRequestBody(String bodyStr, String url, String contentType, String authorization, String ua, String cookie) {
		Map<String, String> headerParams = new ConcurrentHashMap<String, String>(2);
		if (StringUtils.isNotBlank(cookie)) {
			headerParams.put("Cookie", cookie);
		}
		if (StringUtils.isNotBlank(authorization)) {
			headerParams.put("Authorization", authorization);
		}
		return postRequestBody(bodyStr, url, contentType, ua, headerParams);
	}

	/**
	 * @Title: postRequestBody
	 * @Description: TODO
	 * @param bodyStr
	 * @param url
	 * @param contentType
	 * @param ua
	 * @param headerParams
	 * @return String
	 */
	public String postRequestBody(String bodyStr, String url, String contentType, String ua, Map<String, String> headerParams) {
		log.debug(">>>>>>>> bodyStr >> {}", bodyStr);
		log.debug(">>>>>>>> url >> {}", url);
		log.debug(">>>>>>>> contentType >> {}, headerParams >> {}, ua >> {}", contentType, headerParams, ua);
		if (StringUtils.isBlank(url) || StringUtils.isBlank(bodyStr)) {
			return null;
		}
		CloseableHttpClient httpclient = null;
		HttpPost httpPost = null;
		CloseableHttpResponse response = null;
		String httpStr = null;
		try {
			httpclient = createHttpClient(ua);
			httpPost = new HttpPost(url);
			if (StringUtils.isBlank(contentType)) {
				contentType = CONTENT_TYPE_APPLICATION_JSON;
			}
			if (MapUtils.isNotEmpty(headerParams)) {
				for (Map.Entry<String, String> entry : headerParams.entrySet()) {
					String key = entry.getKey();
					String value = entry.getValue();
					httpPost.addHeader(key, value);
				}
			}
			ContentType ct = ContentType.parse(contentType);
			httpPost.setEntity(new StringEntity(bodyStr, ct));
			response = httpclient.execute(httpPost);
			httpStr = EntityUtils.toString(response.getEntity(), "UTF-8");
		} catch (ClientProtocolException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			close(httpclient, response, httpPost);
		}
		return httpStr;
	}

	/**
	 * @Title: postSOAPRequest
	 * @Description: 请求SOAP XML格式
	 * @param soapRequestXML
	 *            SOAP XML文件
	 * @param url
	 * @param ua
	 * @param contentType
	 * @param soapAction
	 * @Reutrn String
	 */
	public String postSOAPRequest(String soapRequestXML, String url, String ua, String contentType, String soapAction) {
		return postSOAPRequest(soapRequestXML, url, ua, contentType, soapAction, null);
	}

	/**
	 * @Title: postSOAPRequest
	 * @Description: 请求SOAP XML格式
	 * @param soapRequestXML
	 *            SOAP XML文件
	 * @param url
	 * @param ua
	 * @param contentType
	 * @param soapAction
	 * @Reutrn String
	 */
	public String postSOAPRequest(String soapRequestXML, String url, String ua, String contentType, String soapAction, String encoding) {
		log.debug(">>>>>>>> soapRequestXML >> {}", soapRequestXML);
		log.debug(">>>>>>>> url >> {}", url);
		log.debug(">>>>>>>> contentType >> {}, soapAction >> {}, ua >> {}, encoding >> {}", contentType, soapAction, ua, encoding);
		encoding = StringUtils.isBlank(encoding) ? "UTF-8" : encoding;
		if (StringUtils.isBlank(url) || StringUtils.isBlank(soapRequestXML)) {
			return null;
		}
		CloseableHttpClient httpclient = null;
		HttpPost httpPost = null;
		CloseableHttpResponse response = null;
		String httpStr = null;
		try {
			httpclient = createHttpClient(ua);
			httpPost = new HttpPost(url);
			if (StringUtils.isNotBlank(soapAction)) {
				httpPost.setHeader("SOAPAction", soapAction);
			}
			if (StringUtils.isBlank(contentType)) {
				contentType = "application/soap+xml;charset=utf-8";
			}
			ContentType ct = ContentType.parse(contentType);
			httpPost.setEntity(new StringEntity(soapRequestXML, ct));
			response = httpclient.execute(httpPost);
			httpStr = EntityUtils.toString(response.getEntity(), encoding);
		} catch (ClientProtocolException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			close(httpclient, response, httpPost);
		}
		return httpStr;
	}

	/**
	 * @Title: postXML
	 * @Description: TODO
	 * @param xml
	 * @param url
	 * @Reutrn String
	 */
	public String postXML(String xml, String url) {
		return postXML(xml, url, CONTENT_TYPE_TEXT_XML);
	}

	/**
	 * @Title: postXML
	 * @Description: TODO
	 * @param xml
	 * @param url
	 * @param contentType
	 * @Reutrn String
	 */
	public String postXML(String xml, String url, String contentType) {
		return postXML(xml, url, contentType, null);
	}

	/**
	 * @Title: postXML
	 * @Description: TODO
	 * @param xml
	 * @param url
	 * @param contentType
	 * @param authorization
	 * @Reutrn String
	 */
	public String postXML(String xml, String url, String contentType, String authorization) {
		return postXML(xml, url, contentType, authorization, null, null);
	}

	/**
	 * @Title: postXML
	 * @Description: TODO
	 * @param xml
	 * @param url
	 * @param contentType
	 * @param authorization
	 * @param ua
	 * @param cookie
	 * @Reutrn String
	 */
	public String postXML(String xml, String url, String contentType, String authorization, String ua, String cookie) {
		log.debug(">>>>>>>> xml >> {}", xml);
		log.debug(">>>>>>>> url >> {}", url);
		log.debug(">>>>>>>> contentType >> {}, authorization >> {}, ua >> {}, cookie >> {}", contentType, authorization, ua, cookie);
		if (StringUtils.isBlank(xml) || StringUtils.isBlank(url)) {
			return null;
		}
		CloseableHttpClient httpclient = null;
		HttpPost httpPost = null;
		CloseableHttpResponse response = null;
		String httpStr = null;
		try {
			httpclient = createHttpClient(ua);
			httpPost = new HttpPost(url);
			if (StringUtils.isNotBlank(cookie)) {
				httpPost.addHeader("Cookie", cookie);
			}
			if (StringUtils.isNotBlank(authorization)) {
				httpPost.setHeader("Authorization", authorization);
			}
			ContentType ct = ContentType.parse(contentType);
			httpPost.setEntity(new StringEntity(xml, ct));
			response = httpclient.execute(httpPost);
			HttpEntity entity = response.getEntity();
			httpStr = EntityUtils.toString(entity, "UTF-8");
		} catch (ClientProtocolException e) {
			log.error(e.getMessage(), e);
		} catch (IOException e) {
			log.error(e.getMessage(), e);
		} catch (Exception e) {
			log.error(e.getMessage(), e);
		} finally {
			close(httpclient, response, httpPost);
		}
		return httpStr;
	}

	/**
	 * @Title: close
	 * @Description: TODO
	 * @param httpclient
	 * @param response
	 * @param httpRequest
	 * @return void
	 */
	public void close(CloseableHttpClient httpclient, CloseableHttpResponse response, HttpRequestBase httpRequest) {
		if (response != null) {
			try {
				EntityUtils.consume(response.getEntity());
				response.close();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
			}
		}
		if (null != httpRequest) {
			httpRequest.releaseConnection();
		}
		if (null != httpclient) {
			try {
				httpclient.close();
			} catch (IOException e) {
				log.error(e.getMessage(), e);
			}
		}
	}

	/**
	 * @Title: getRandomUA
	 * @Description: 随机获取UA
	 * @return String
	 */
	public static String getRandomUA() {
		Random random = new Random();
		int index = random.nextInt(UA_ARR.length);
		return UA_ARR[index];
	}
}